using Assets.Scripts.GDK;
using System;
using System.Collections.Generic;

namespace Brick
{
	public class gdk_EventManager
	{
		public class BroadcastException : Exception
		{
			public BroadcastException(string msg)
				: base(msg)
			{
			}
		}

		public class ListenerException : Exception
		{
			public ListenerException(string msg)
				: base(msg)
			{
			}
		}

		public static gdk_EventManager _instance;

		private Dictionary<string, Delegate> myEventCallback = new Dictionary<string, Delegate>();

		public static gdk_EventManager Instance
		{
			get
			{
				if (_instance == null)
				{
					_instance = new gdk_EventManager();
				}
				return _instance;
			}
			set
			{
				_instance = value;
			}
		}

		public void AddEventListener(string eventType, Callback handler)
		{
			OnListenerAdding(eventType, handler);
			myEventCallback[eventType] = (Callback)Delegate.Combine((Callback)myEventCallback[eventType], handler);
		}

		public void AddEventListener<T>(string eventType, Callback<T> handler)
		{
			OnListenerAdding(eventType, handler);
			myEventCallback[eventType] = (Callback<T>)Delegate.Combine((Callback<T>)myEventCallback[eventType], handler);
		}

		public void AddEventListener<T, T2>(string eventType, Callback<T, T2> handler)
		{
			OnListenerAdding(eventType, handler);
			myEventCallback[eventType] = (Callback<T, T2>)Delegate.Combine((Callback<T, T2>)myEventCallback[eventType], handler);
		}

		public void RemoveEventListener(string eventType, Callback handler)
		{
			OnListenerRemoving(eventType, handler);
			myEventCallback[eventType] = (Callback)Delegate.Remove((Callback)myEventCallback[eventType], handler);
			OnListenerRemoved(eventType);
		}

		public void RemoveEventListener<T>(string eventType, Callback<T> handler)
		{
			OnListenerRemoving(eventType, handler);
			myEventCallback[eventType] = (Callback<T>)Delegate.Remove((Callback<T>)myEventCallback[eventType], handler);
			OnListenerRemoved(eventType);
		}

		public void RemoveEventListener<T, T2>(string eventType, Callback<T, T2> handler)
		{
			OnListenerRemoving(eventType, handler);
			myEventCallback[eventType] = (Callback<T, T2>)Delegate.Remove((Callback<T, T2>)myEventCallback[eventType], handler);
			OnListenerRemoved(eventType);
		}

		public void DispachEvent(string eventType)
		{
			OnBroadcasting(eventType);
			if (myEventCallback.TryGetValue(eventType, out Delegate value))
			{
				Callback callback = value as Callback;
				if (callback == null)
				{
					throw CreateBroadcastSignatureException(eventType);
				}
				callback();
			}
		}

		public void DispachEvent<T>(string eventType, T arg1)
		{
			if (!myEventCallback.ContainsKey(eventType))
			{
				return;
			}
			OnBroadcasting(eventType);
			if (myEventCallback.TryGetValue(eventType, out Delegate value))
			{
				Callback<T> callback = value as Callback<T>;
				if (callback == null)
				{
					throw CreateBroadcastSignatureException(eventType);
				}
				callback(arg1);
			}
		}

		public void DispachEvent<T, T2>(string eventType, T arg1, T2 arg2)
		{
			if (!myEventCallback.ContainsKey(eventType))
			{
				return;
			}
			OnBroadcasting(eventType);
			if (myEventCallback.TryGetValue(eventType, out Delegate value))
			{
				Callback<T, T2> callback = value as Callback<T, T2>;
				if (callback == null)
				{
					throw CreateBroadcastSignatureException(eventType);
				}
				callback(arg1, arg2);
			}
		}

		public BroadcastException CreateBroadcastSignatureException(string eventType)
		{
			return new BroadcastException($"Broadcasting message \"{eventType}\" but listeners have a different signature than the broadcaster.");
		}

		public void OnListenerAdding(string eventType, Delegate listenerBeingAdded)
		{
			if (!myEventCallback.ContainsKey(eventType))
			{
				myEventCallback.Add(eventType, null);
			}
			Delegate @delegate = myEventCallback[eventType];
			if ((object)@delegate != null && @delegate.GetType() != listenerBeingAdded.GetType())
			{
				throw new ListenerException($"Attempting to add listener with inconsistent signature for event type {eventType}. Current listeners have type {@delegate.GetType().Name} and listener being added has type {listenerBeingAdded.GetType().Name}");
			}
		}

		public void OnBroadcasting(string eventType)
		{
			if (!myEventCallback.ContainsKey(eventType))
			{
				throw new BroadcastException($"Broadcasting message \"{eventType}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.");
			}
		}

		public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved)
		{
			if (myEventCallback.ContainsKey(eventType))
			{
				Delegate @delegate = myEventCallback[eventType];
				if ((object)@delegate == null)
				{
					throw new ListenerException($"Attempting to remove listener with for event type \"{eventType}\" but current listener is null.");
				}
				if (@delegate.GetType() != listenerBeingRemoved.GetType())
				{
					throw new ListenerException($"Attempting to remove listener with inconsistent signature for event type {eventType}. Current listeners have type {@delegate.GetType().Name} and listener being removed has type {listenerBeingRemoved.GetType().Name}");
				}
				return;
			}
			throw new ListenerException($"Attempting to remove listener for type \"{eventType}\" but Messenger doesn't know about this event type.");
		}

		public void OnListenerRemoved(string eventType)
		{
			if ((object)myEventCallback[eventType] == null)
			{
				myEventCallback.Remove(eventType);
			}
		}
	}
}

